/* initGeetest 1.0.0
 * 用于加载id对应的验证码库，并支持宕机模式
 * 暴露 initGeetest 进行验证码的初始化
 * 一般不需要用户进行修改
 */
(function (global, factory) {
  "use strict";
  if (typeof module === "object" && typeof module.exports === "object") {
      // CommonJS
      module.exports = global.document ?
          factory(global, true) :
          function (w) {
              if (!w.document) {
                  throw new Error("Geetest requires a window with a document");
              }
              return factory(w);
          };
  } else {
      factory(global);
  }
})(typeof window !== "undefined" ? window : this, function (window, noGlobal) {
  "use strict";
  if (typeof window === 'undefined') {
      throw new Error('Geetest requires browser environment');
  }
  var document = window.document;
  var Math = window.Math;
  var head = document.getElementsByTagName("head")[0];

  function _Object(obj) {
      this._obj = obj;
  }

  _Object.prototype = {
      _each: function (process) {
          var _obj = this._obj;
          for (var k in _obj) {
              if (_obj.hasOwnProperty(k)) {
                  process(k, _obj[k]);
              }
          }
          return this;
      }
  };
  function Config(config) {
      var self = this;
      new _Object(config)._each(function (key, value) {
          self[key] = value;
      });
  }

  Config.prototype = {
      api_server: 'api.geetest.com',
      protocol: 'http://',
      type_path: '/gettype.php',
      fallback_config: {
          slide: {
              static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
              type: 'slide',
              slide: '/static/js/geetest.0.0.0.js'
          },
          fullpage: {
              static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
              type: 'fullpage',
              fullpage: '/static/js/fullpage.0.0.0.js'
          }
      },
      _get_fallback_config: function () {
          var self = this;
          if (isString(self.type)) {
              return self.fallback_config[self.type];
          } else if (self.new_captcha) {
              return self.fallback_config.fullpage;
          } else {
              return self.fallback_config.slide;
          }
      },
      _extend: function (obj) {
          var self = this;
          new _Object(obj)._each(function (key, value) {
              self[key] = value;
          })
      }
  };
  var isNumber = function (value) {
      return (typeof value === 'number');
  };
  var isString = function (value) {
      return (typeof value === 'string');
  };
  var isBoolean = function (value) {
      return (typeof value === 'boolean');
  };
  var isObject = function (value) {
      return (typeof value === 'object' && value !== null);
  };
  var isFunction = function (value) {
      return (typeof value === 'function');
  };
  var callbacks = {};
  var status = {};
  var random = function () {
      return parseInt(Math.random() * 10000) + (new Date()).valueOf();
  };
  var loadScript = function (url, cb) {
      var script = document.createElement("script");
      script.charset = "UTF-8";
      script.async = true;
      script.onerror = function () {
          cb(true);
      };
      var loaded = false;
      script.onload = script.onreadystatechange = function () {
          if (!loaded &&
              (!script.readyState ||
              "loaded" === script.readyState ||
              "complete" === script.readyState)) {

              loaded = true;
              setTimeout(function () {
                  cb(false);
              }, 0);
          }
      };
      script.src = url;
      head.appendChild(script);
  };
  var normalizeDomain = function (domain) {
      return domain.replace(/^https?:\/\/|\/$/g, '');
  };
  var normalizePath = function (path) {
      path = path.replace(/\/+/g, '/');
      if (path.indexOf('/') !== 0) {
          path = '/' + path;
      }
      return path;
  };
  var normalizeQuery = function (query) {
      if (!query) {
          return '';
      }
      var q = '?';
      new _Object(query)._each(function (key, value) {
          if (isString(value) || isNumber(value) || isBoolean(value)) {
              q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
          }
      });
      if (q === '?') {
          q = '';
      }
      return q.replace(/&$/, '');
  };
  var makeURL = function (protocol, domain, path, query) {
      domain = normalizeDomain(domain);

      var url = normalizePath(path) + normalizeQuery(query);
      if (domain) {
          url = protocol + domain + url;
      }

      return url;
  };
  var load = function (protocol, domains, path, query, cb) {
      var tryRequest = function (at) {

          var url = makeURL(protocol, domains[at], path, query);
          loadScript(url, function (err) {
              if (err) {
                  if (at >= domains.length - 1) {
                      cb(true);
                  } else {
                      tryRequest(at + 1);
                  }
              } else {
                  cb(false);
              }
          });
      };
      tryRequest(0);
  };
  var jsonp = function (domains, path, config, callback) {
      if (isObject(config.getLib)) {
          config._extend(config.getLib);
          callback(config);
          return;
      }
      if (config.offline) {
          callback(config._get_fallback_config());
          return;
      }
      var cb = "geetest_" + random();
      window[cb] = function (data) {
          if (data.status === 'success') {
              callback(data.data);
          } else if (!data.status) {
              callback(data);
          } else {
              callback(config._get_fallback_config());
          }
          window[cb] = undefined;
          try {
              delete window[cb];
          } catch (e) {
          }
      };
      load(config.protocol, domains, path, {
          gt: config.gt,
          callback: cb
      }, function (err) {
          if (err) {
              callback(config._get_fallback_config());
          }
      });
  };
  var throwError = function (errorType, config) {
      var errors = {
          networkError: '网络错误'
      };
      if (typeof config.onError === 'function') {
          config.onError(errors[errorType]);
      } else {
          throw new Error(errors[errorType]);
      }
  };
  var detect = function () {
      return !!window.Geetest;
  };
  if (detect()) {
      status.slide = "loaded";
  }
  var initGeetest = function (userConfig, callback) {
      var config = new Config(userConfig);
      if (userConfig.https) {
          config.protocol = 'https://';
      } else if (!userConfig.protocol) {
          config.protocol = window.location.protocol + '//';
      }
      jsonp([config.api_server || config.apiserver], config.type_path, config, function (newConfig) {
          var type = newConfig.type;
          var init = function () {
              config._extend(newConfig);
              callback(new window.Geetest(config));
          };
          callbacks[type] = callbacks[type] || [];
          var s = status[type] || 'init';
          if (s === 'init') {
              status[type] = 'loading';
              callbacks[type].push(init);
              load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
                  if (err) {
                      status[type] = 'fail';
                      throwError('networkError', config);
                  } else {
                      status[type] = 'loaded';
                      var cbs = callbacks[type];
                      for (var i = 0, len = cbs.length; i < len; i = i + 1) {
                          var cb = cbs[i];
                          if (isFunction(cb)) {
                              cb();
                          }
                      }
                      callbacks[type] = [];
                  }
              });
          } else if (s === "loaded") {
              init();
          } else if (s === "fail") {
              throwError('networkError', config);
          } else if (s === "loading") {
              callbacks[type].push(init);
          }
      });
  };
  window.initGeetest = initGeetest;
  return initGeetest;
});
